﻿/*
Copyright (c) 2009 Vratislav Cermak (vratacermak@gmail.com)
 
This file is part of TeachMe Project. See http://code.google.com/p/teach-me for more information.
 
TeachMe is an open source project. Using of TeachMe is free of charge.
It is distributed under GNU General Public License version 3 (GPLv3).
GPLv3 can be found at http://www.gnu.org/licenses/gpl.html.
 
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using ICSharpCode.SharpZipLib.Zip;
using TeachMe.Application.InlineTests;

namespace TeachMe.Application
{
    public class Utilities
    {
        private const int ReadBufferSize = 2048;

#if DEBUG
        static Utilities()
        {
            Runner.Test(typeof(Utilities));
        }
#endif

        public static string GetRandomFileName(string directory, string extension)
        {
            Random random = new Random(DateTime.Now.Millisecond);
            while (true)
            {
                string fileName = String.Format("{0}{1}", Guid.NewGuid().ToString().Replace('-', random.Next(9).ToString()[0]).ToUpper(), extension);
                string path = Path.Combine(directory, fileName);
                if (!File.Exists(path))
                {
                    return path;
                }
            }
        }

        public static string GetTempOutputDirectory()
        {
            string pathPrefix = Path.Combine(Path.GetTempPath(), new Regex("\\W").Replace(DateTime.Now.ToString("s"), "-"));
            Random random = new Random(DateTime.Now.Millisecond);

            while (true)
            {
                string path = String.Format("{0}-{1}", pathPrefix, random.Next(Int32.MaxValue));

                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);

                    return path;
                }
            }
        }

        public static void Unzip(string outputDirectory, string archivePath)
        {
            if (!Directory.Exists(outputDirectory))
            {
                Directory.CreateDirectory(outputDirectory);
            }

            using (FileStream archive = File.OpenRead(archivePath))
            {
                Unzip(archive, outputDirectory);
            }
        }

        public static string Unzip(string outputDirectory, Stream archiveStream)
        {
            Unzip(archiveStream, outputDirectory);

            return outputDirectory;
        }

        private static void Unzip(Stream archive, string outputDirectory)
        {
            using (ZipInputStream zipInputStream = new ZipInputStream(archive))
            {
                while (true)
                {
                    ZipEntry zipEntry = zipInputStream.GetNextEntry();

                    if (zipEntry == null)
                    {
                        break;
                    }

                    if (zipEntry.IsDirectory)
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(outputDirectory, zipEntry.Name)));
                    }
                    else
                    {
                        if (!String.IsNullOrEmpty(zipEntry.Name))
                        {
                            byte[] buffer = new byte[ReadBufferSize];

                            // TODO: test if it works with files in subdirectories

                            using (FileStream myFileStream = File.Create(Path.Combine(outputDirectory, zipEntry.Name)))
                            {
                                while (true)
                                {
                                    int readLen = zipInputStream.Read(buffer, 0, ReadBufferSize);

                                    if (readLen <= 0)
                                    {
                                        break;
                                    }

                                    myFileStream.Write(buffer, 0, readLen);
                                }
                            }
                        }
                    }
                }
            }
        }

        public static Point SubstractLocations(Point baseValue, Point subtrahend)
        {
            return new Point(baseValue.X - subtrahend.X, baseValue.Y - subtrahend.Y);
        }

        public static Point AddUpLocations(Point first, Point second)
        {
            return new Point(first.X + second.X, first.Y + second.Y);
        }

        public static bool ReadNodeValue(XmlReader reader, string name, ref string value)
        {
            if (reader.Name == name)
            {
                while (reader.Read() && !(reader.NodeType == XmlNodeType.EndElement && reader.Name == name))
                {
                    if (reader.NodeType == XmlNodeType.Text && reader.HasValue)
                    {
                        value = reader.Value;
                        return true;
                    }
                }
            }
            return false;
        }

        // TODO: don't like this method
        public static bool IsNotEmpty(params TextBox[] textBoxes)
        {
            foreach (var textBox in textBoxes)
            {
                if (textBox.Text.Trim().Length == 0)
                {
                    return false;
                }
            }
            return true;
        }

        // TODO: don't like this method
        public static bool IsNotEmpty(params string[] texts)
        {
            foreach (var text in texts)
            {
                if (text == null)
                {
                    return false;
                }
                if (text.Trim().Length == 0)
                {
                    return false;
                }
            }
            return true;
        }

        public static bool Equals(string first, string second)
        {
            if (!IsNotEmpty(first) && !IsNotEmpty(second))
            {
                return true;
            }

            if (!IsNotEmpty(first) || !IsNotEmpty(second))
            {
                return false;
            }

            return String.Compare(first.Trim(), second.Trim(), true) == 0;
        }

        public static List<int> ParseToNumbers(string text)
        {
            List<int> result = new List<int>();

            if (!String.IsNullOrEmpty(text))
            {
                string[] items = text.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries);

                foreach (string item in items)
                {
                    int number;
                    if (Int32.TryParse(item, out number))
                    {
                        result.Add(number);
                    }
                }

                result.Sort();
            }

            return result;
        }

        public static IEnumerable<T> GetControls<T>(Control parent) where T : Control
        {
            List<Control> parents = new List<Control> { parent };

            while (parents.Count > 0)
            {
                foreach (Control control in parents[0].Controls)
                {
                    if (control is T)
                    {
                        yield return control as T;
                    }

                    if (control.Controls.Count > 0)
                    {
                        parents.Add(control);
                    }
                }

                parents.RemoveAt(0);
            }
        }

        public static string ReadTextResource(string name)
        {
            Assembly executingAssembly = Assembly.GetExecutingAssembly();
            executingAssembly.GetManifestResourceStream(name);

            using (Stream stream = executingAssembly.GetManifestResourceStream(name))
            {
                if (stream != null && stream.CanRead)
                {
                    using (TextReader reader = new StreamReader(stream))
                    {
                        return reader.ReadToEnd();
                    }
                }
            }

            return String.Empty;
        }

        public static T Deserialize<T>(string xml) where T : class
        {
            T data = null;

            try
            {
                Logging.Debug(typeof(T), "Loading xml.");

                XmlSerializer serializer = new XmlSerializer(typeof(T));
                using (StringReader stream = new StringReader(xml))
                {
                    data = serializer.Deserialize(stream) as T;
                }
            }
            catch (Exception e)
            {
                Logging.Error(typeof(T), e, "Xml could not be loaded.");
            }

            return data;
        }

        public static string ConvertToPascalCase(string str)
        {
            if (String.IsNullOrEmpty(str))
            {
                return str;
            }

            StringBuilder builder = new StringBuilder();

            foreach (char character in str)
            {
                if ((builder.Length == 0 || !Char.IsLetter(builder[builder.Length - 1])) && Char.IsLetter(character))
                {
                    builder.Append(Char.ToUpper(character));
                }
                else
                {
                    builder.Append(Char.ToLower(character));
                }
            }

            return builder.ToString();
        }

        [Test(Result = 0, Parameters = new object[] { "0.1.9.0", "0.1.9.0" })]
        [Test(Result = -1, Parameters = new object[] { "0.1.8.0", "0.1.9.0" })]
        [Test(Result = -1, Parameters = new object[] { "0.1.8.9", "0.1.9.0" })]
        [Test(Result = 1, Parameters = new object[] { "0.1.10.0", "0.1.9.99" })]
        [Test(Result = 0, Parameters = new object[] { null, null })]
        [Test(Result = -1, Parameters = new object[] { null, "0.1.9.0" })]
        [Test(Result = 1, Parameters = new object[] { "0.1.9.0", null })]
        public static int CompareVersions(string first, string second)
        {
            if (String.IsNullOrEmpty(first) && String.IsNullOrEmpty(second))
            {
                return 0;
            }
            if (String.IsNullOrEmpty(first))
            {
                return -1;
            }
            if (String.IsNullOrEmpty(second))
            {
                return 1;
            }

            string[] firstSplitted = first.Split('.');
            string[] secondSplitted = second.Split('.');

            for (int idx = 0; idx < Math.Min(firstSplitted.Length, secondSplitted.Length); idx++)
            {
                int firstNumber = Int32.Parse(firstSplitted[idx]);
                int secondNumber = Int32.Parse(secondSplitted[idx]);

                if (firstNumber < secondNumber)
                {
                    return -1;
                }
                if (firstNumber > secondNumber)
                {
                    return 1;
                }
            }

            return 0;
        }
    }
}
